#include "KDMDetectorConstruction.hh"
#include "KDMGlobals.hh"
#include "G4Element.hh"
#include "G4Material.hh"
#include "G4Box.hh"
#include "G4Tubs.hh"
#include "G4LogicalVolume.hh"
#include "G4ThreeVector.hh"
#include "G4PVPlacement.hh"
#include "G4SDManager.hh"
#include "G4NistManager.hh"
#include <cmath>
#include <sstream>
#include <iostream>
using namespace std;

KDMDetectorConstruction::KDMDetectorConstruction() {
	;
}

KDMDetectorConstruction::~KDMDetectorConstruction() {
	;
}

G4VPhysicalVolume *KDMDetectorConstruction::Construct() {
	// Define materials

	G4NistManager* nistMan = G4NistManager::Instance();

	G4Element* N = new G4Element("Nitrogen", "N", 7, 14.01 * g / mole);
	G4Element* O = new G4Element("Oxygen", "O", 8, 16.00 * g / mole);
	G4Material* Air = new G4Material("Air", 15 * g / 6.022 / pow(10, 23) / cm3, 2);
	Air->AddElement(N, 70 * perCent);
	Air->AddElement(O, 30 * perCent);

	G4Element* H = new G4Element("Hydrogen", "H", 1, 1.01 * g / mole);
	G4Element* C = new G4Element("Carbon", "C", 3, 12.01 * g / mole);
	G4Material* Plastic = new G4Material("Plastic", 1.032 * g / cm3, 2);
	Plastic->AddElement(H, 52.4 * perCent);
	Plastic->AddElement(C, 47.6 * perCent);

	G4Material* CsI = nistMan->FindOrBuildMaterial("G4_CESIUM_IODIDE");

	// Define world

	G4Box* spaceSolid = new G4Box("spaceSolid", 2.6 * m, 2.6 * m, 10.1 * m);
	G4LogicalVolume* spaceLog = new G4LogicalVolume(spaceSolid, Air, "spaceLog");
	G4VPhysicalVolume* spacePhys = new G4PVPlacement(0, G4ThreeVector(0, 0, 0), spaceLog, "space", 0, 0, 0);

	G4Box* worldSolid = new G4Box("worldSolid", 2.5 * m, 2.5 * m, 10 * m);
	G4LogicalVolume* worldLog = new G4LogicalVolume(worldSolid, Air, "worldLog");
	G4VPhysicalVolume* worldPhys = new G4PVPlacement(0, G4ThreeVector(0, 0, 0), worldLog, "world", spaceLog, 0, 0);
	(void) worldPhys;

	// Define crystal block

	G4Tubs* crystalBlockSolid = new G4Tubs("crystalBlockSolid", 0, 0.95 * m, 25 * cm, 0, 360 * deg);
	G4LogicalVolume* crystalBlockLog = new G4LogicalVolume(crystalBlockSolid, Air, "crystalBlockLog");
	G4VPhysicalVolume* crystalBlockPhys = new G4PVPlacement(0, G4ThreeVector(0, 0, 25 * cm), crystalBlockLog,
			"crystalBlock", worldLog, 0, 0);
	(void) crystalBlockPhys;

	// Define small crystals

	G4Box* smallCrystalSolid = new G4Box("smallCrystalSolid", 1.25 * cm, 1.25 * cm, 25 * cm);
	G4LogicalVolume* smallCrystalLog = new G4LogicalVolume(smallCrystalSolid, CsI, "smallCrystalLog");

	int smallCrystalDimension = 48;
	int count = 0;
	for (int i = 0; i < smallCrystalDimension; i++) {
		for (int j = 0; j < smallCrystalDimension; j++) {
			double xPos = (i - smallCrystalDimension / 2 + .5) * 2.5 * cm;
			double yPos = (j - smallCrystalDimension / 2 + .5) * 2.5 * cm;
			if (fabs(xPos) > 10 * cm || fabs(yPos) > 10 * cm) {
				std::stringstream ss;
				ss << "smallCrystal" << i << "," << j;
				G4String name = ss.str();
				G4VPhysicalVolume* smallCrystalPhys = new G4PVPlacement(0, G4ThreeVector(xPos, yPos, 0),
						smallCrystalLog, name, crystalBlockLog, 0, 0);
				(void) smallCrystalPhys;
				count++;
			}
		}
	}
	cout << count << endl;

	// Define large crystals

	G4Box* largeCrystalSolid = new G4Box("largeCrystalSolid", 2.5 * cm, 2.5 * cm, 25 * cm);
	G4LogicalVolume* largeCrystalLog = new G4LogicalVolume(largeCrystalSolid, CsI, "largeCrystalLog");

	int largeCrystalDimension = 36;
	count = 0;
	for (int i = 0; i < largeCrystalDimension; i++) {
		for (int j = 0; j < largeCrystalDimension; j++) {
			double xPos = (i - largeCrystalDimension / 2 + .5) * 5 * cm;
			double yPos = (j - largeCrystalDimension / 2 + .5) * 5 * cm;
			if (fabs(xPos) > 0.6 * m || fabs(yPos) > 0.6 * m) {
				if (sqrt(pow(fabs(xPos) + 2.5 * cm, 2) + pow(fabs(yPos) + 2.5 * cm, 2)) < 0.95 * m) {
					std::stringstream ss;
					ss << "largeCrystal" << i << "," << j;
					G4String name = ss.str();
					G4VPhysicalVolume* largeCrystalPhys = new G4PVPlacement(0, G4ThreeVector(xPos, yPos, 0),
							largeCrystalLog, name, crystalBlockLog, 0, 0);
					(void) largeCrystalPhys;
					count++;
				}
			}
		}
	}
	cout << count << endl;

	// Define CV

	G4Tubs* vetoSolid = new G4Tubs("vetoSolid", 0, 0.95 * m, 1.5 * mm, 0, 360 * deg);
	G4LogicalVolume* frontVetoLog = new G4LogicalVolume(vetoSolid, Plastic, "frontVetoLog");
	G4VPhysicalVolume* frontVetoPhys = new G4PVPlacement(0, G4ThreeVector(0, 0, -251.5 * mm), frontVetoLog,
			"frontVeto", worldLog, 0, 0);
	(void) frontVetoPhys;

	G4LogicalVolume* rearVetoLog = new G4LogicalVolume(vetoSolid, Plastic, "rearVetoLog");
	G4VPhysicalVolume* rearVetoPhys = new G4PVPlacement(0, G4ThreeVector(0, 0, -1.5 * mm), rearVetoLog, "rearVeto",
			worldLog, 0, 0);
	(void) rearVetoPhys;

	/*	G4Box* frontHoleSolid = new G4Box("frontHoleSolid", 12 * cm, 12 * cm, 1.5 * mm);
	 G4LogicalVolume* frontHoleLog = new G4LogicalVolume(frontHoleSolid, Air, "frontHoleLog");
	 G4VPhysicalVolume* frontHolePhys = new G4PVPlacement(0, G4ThreeVector(0, 0, 0), frontHoleLog, "frontHole",
	 frontVetoLog, 0, 0);
	 (void) frontHolePhys;

	 G4Box* rearHoleSolid = new G4Box("rearHoleSolid", 7.5 * cm, 7.5 * cm, 1.5 * mm);
	 G4LogicalVolume* rearHoleLog = new G4LogicalVolume(rearHoleSolid, Air, "rearHoleLog");
	 G4VPhysicalVolume* rearHolePhys = new G4PVPlacement(0, G4ThreeVector(0, 0, 0), rearHoleLog, "rearHole",
	 rearVetoLog, 0, 0);
	 (void) rearHolePhys;*/

	return spacePhys;
}
